package com.dl.test.config;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;

/**
 * 线程池配置类
 * @author DL
 * @create 2022-05-10 10:37
 */
@Data
@Configuration
@Slf4j
@ConfigurationProperties(prefix = "async.executor.thread")
public class ThreadPoolConfig {

    // 核心线程数
    public int corePoolSize = 3;

    // 最大线程数
    public int maxPoolSize;

    public int queueCapacity;

    public String threadPrefix;

    public int keepAliveSeconds;

    /**
     * corePoolSize = 1
     * queueCapacity = Integer.MAX_VALUE
     * maxPoolSize = Integer.MAX_VALUE
     * keepAliveTime = 60秒
     * allowCoreThreadTimeout = false
     * rejectedExecutionHandler = AbortPolicy()
     *
     * 当线程数小于核心线程数时，会一直创建线程直到线程数等于核心线程数；
     * 当线程数等于核心线程数时，新加入的任务会被放到任务队列等待执行；
     * 当任务队列已满，又有新的任务时，会创建线程直到线程数量等于最大线程数；
     * 当线程数等于最大线程数，且任务队列已满时，新加入任务会被拒绝。
     *
     * AbortPolicy - 抛出异常，中止任务。抛出拒绝执行 RejectedExecutionException 异常信息。线程池默认的拒绝策略。必须处理好抛出的异常，否则会打断当前的执行流程，影响后续的任务执行
     * CallerRunsPolicy - 如果添加到线程池失败，那么主线程会自己去执行该任务
     * DiscardPolicy - 直接丢弃，其他啥都没有
     * DiscardOldestPolicy - 丢弃队列最老任务，添加新任务。当触发拒绝策略，只要线程池没有关闭的话，丢弃阻塞队列 workQueue 中最老的一个任务，并将新任务加入
    */

    @Bean
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数
        executor.setCorePoolSize(corePoolSize);
        // 设置最大线程数
        executor.setMaxPoolSize(maxPoolSize);
        // 设置队列容量
        executor.setQueueCapacity(queueCapacity);
        // 设置线程活跃时间（秒）
        executor.setKeepAliveSeconds(keepAliveSeconds);
        // 设置默认线程名称
        executor.setThreadNamePrefix(threadPrefix);
        // 设置拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        // 允许核心线程超关闭，直到线程数为0
        executor.setAllowCoreThreadTimeOut(true);
        executor.initialize();
        log.info("\n创建线程池完成，参数为== 核心线程数：{}，最大线程数：{}，队列容量：{}，线程活跃时间：{}秒，默认线程名称：{}，", corePoolSize, maxPoolSize, queueCapacity, keepAliveSeconds, threadPrefix);
        return executor;
    }
}
